using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace BAFIRCSharp
{
    public class IRCNetwork
    {
        // Connection stuff
        public List<ServerInfo> Servers = null;
        public int CurServer = 0;
        public IRCConnection Connection = null;
        public IdentD Ident = null;
        public bool Online = false;

        // Currently active stuff
        public List<IRCChannel> Channels = null;
        public List<IRCQuery> Queries = null;

        // Management stuff
        private Thread IRCThread = null;
        public bool Alive { get { return IRCThread.IsAlive; } }

        // Configuration stuff
        public List<string> Nicks = null;
        public int CurNick = 0;
        public string Username = "";
        public string Password = "";
        public string Realname = "";
        public bool EnableIdent = false;
        public int Lag = 0;
        public DateTime LastLagCheck = DateTime.Now;

        public IRCNetwork()
        {
            Servers = new List<ServerInfo>();
            Connection = new IRCConnection();
            Ident = new IdentD();
            Channels = new List<IRCChannel>();
            Queries = new List<IRCQuery>();
            Nicks = new List<string>();
            IRCThread = new Thread(new ThreadStart(IRCThreadF));
        }

        public bool Connect()
        {
            if (!DoConnect())
                return false;

            IRCThread.Start();

            return true;
        }

        public bool DoConnect()
        {
            if (Servers.Count <= 0)
                return false;

            if (CurServer >= Servers.Count)
                CurServer %= Servers.Count;
            if (CurNick >= Nicks.Count)
                CurNick %= Nicks.Count;

            if (!Ident.Active && EnableIdent)
            {
                Ident.Ident = Username;
                Ident.OS = "UNIX";
                Ident.OneShot = true;
                Ident.StartIdentD();
            }

            if (!Connection.Connect(Servers[CurServer]))
            {
                CurServer = ++CurServer % Servers.Count;
                return false;
            }

            Connection.SendMessage(new BAFIRCSharp.Messages.User(Username, "localhost", Servers[CurServer].Server, Realname));
            Connection.SendMessage(new BAFIRCSharp.Messages.Nick(Nicks[CurNick]));

            foreach (IRCChannel Chan in Channels)
                Connection.SendMessage(new BAFIRCSharp.Messages.Join(Chan.ChannelName)); // add keyword support

            return true;
        }

        
        public void IRCThreadF()
        {
            bool Running = (Connection != null);
            while (Running)
            {
                if (Connection.Poll())
                {
                    IRCMessage Message;
                    while ((Message = Connection.GetMessage()) != null)
                    {
                        if (Message.IsNumeric)
                        {
                            IRCChannel Chan = null;
                            Console.WriteLine((NumericReplies)Message.CommandNumeric);
                            switch ((NumericReplies)Message.CommandNumeric)
                            {
                                case NumericReplies.RPL_WELCOME:
                                    Online = true;
                                    break;
                                case NumericReplies.ERR_NICKNAMEINUSE:
                                    if (!Online)
                                    {
                                        CurNick = ++CurNick % Nicks.Count;
                                        Connection.SendMessage(new BAFIRCSharp.Messages.Nick(Nicks[CurNick]));
                                    }
                                    break;
                                case NumericReplies.RPL_TOPIC:
                                    Chan = FindChannel(Message.Params[0]);
                                    if (Chan != null)
                                    {
                                        Chan.NewTopic(Message.Sender, Message.Params[1]);
                                        Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg("#iia", "Topic for " + Message.Params[0] + ": " + Message.Params[1] + " -- set by " + Message.Sender.Nickname));
                                    }
                                    break;

                                case NumericReplies.RPL_NAMREPLY:
                                    Console.WriteLine(Message.Message);
                                    Chan = FindChannel(Message.Params[2]);
                                    if (Chan != null)
                                    {
                                        if (Chan.UserListComplete) // update the list
                                        {
                                            Chan.Users.Clear();
                                            Chan.UserListComplete = false;
                                        }
                                        // Parse out the nick list.
                                        // "( '=' / '*' / '@' ) <channel> :[ '@' / '+' ] <nick> *( ' ' [ '@' / '+' ] <nick> )"
                                        char[] Sep = { ' ' };
                                        string[] Users = Message.Params[3].Split(Sep);
                                        foreach (string User in Users)
                                        {
                                            IRCChannelUserStatus UserStatus = IRCChannelUserStatus.Normal;
                                            string Nick;
                                            if (User.StartsWith("@"))
                                            {
                                                UserStatus = IRCChannelUserStatus.Op;
                                                Nick = User.Substring(1);
                                            }
                                            else if (User.StartsWith("+"))
                                            {
                                                UserStatus = IRCChannelUserStatus.Voice;
                                                Nick = User.Substring(1);
                                            }
                                            else
                                                Nick = User;

                                            Chan.Users.Add(new IRCChannelUser(Nick, UserStatus));
                                            Console.WriteLine(User);
                                        }
                                    }
                                    break;
                                case NumericReplies.RPL_ENDOFNAMES:
                                    Chan = FindChannel(Message.Params[1]);
                                    if (Chan != null)
                                        Chan.UserListComplete = true;
                                    break;
                            }
                        }
                        else
                        {
                            switch(Message.Command)
                            {
                                case "JOIN":
                                    if (Message.Sender.Nickname.ToLower() == Nicks[CurNick].ToLower())
                                    {
                                        Channels.Add(new IRCChannel(Message.Params[0]));
                                        Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg("#iia", "New channel joined: " + Message.Params[0]));
                                    }
                                    else
                                    {
                                        IRCChannel Chan = FindChannel(Message.Params[0]);
                                        if (Chan != null)
                                        {
                                            Chan.NewJoin(Message.Sender);
                                            Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg("#iia", Message.Sender.Nickname + " joined " + Message.Params[0] + " .. duh?"));
                                        }
                                    }
                                    break;

                                case "PART":
                                    if (Message.Sender.Nickname.ToLower() == Nicks[CurNick].ToLower())
                                    {
                                        Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg("#iia", "New channel parted: " + Message.Params[0]));
                                        Channels.Remove(FindChannel(Message.Params[0]));
                                    }
                                    else
                                    {
                                        IRCChannel Chan = FindChannel(Message.Params[0]);
                                        if (Chan != null)
                                        {
                                            Chan.NewPart(Message.Sender, ((Message.Params.Count > 1) ? Message.Params[1] : ""));
                                            Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg("#iia", Message.Sender.Nickname + " parted " + Message.Params[0] + " .. duh?"));
                                        }
                                    }
                                    break;

                                case "PRIVMSG":
                                    if (Message.Params[1].ToLower() == "!test")
                                        Connection.SendMessage("PRIVMSG " + Message.Params[0] + " :FUCK YOU " + Message.Sender.Nickname + "\r\n");
                                    if (Message.Params[1].Length > 5 && Message.Params[1].Substring(0, 5).ToLower() == "!bcom")
                                    {
                                        //if (!msg.Sender.Nickname.ToLower().Contains("baf"))
                                        if (Message.Sender.Hostname.ToLower() != "allegro/botmaster/baf" && Message.Sender.Hostname.ToLower() != "allegro/user/sevalecan")
                                            Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg(Message.Params[0], "You aren't authorized."));
                                        else if (Message.Params[1].Length > 7)
                                            Connection.SendMessage(Message.Params[1].Substring(6) + "\r\n");
                                    }
                                    if (Message.Params[1].Length >= 4 && Message.Params[1].Substring(0, 4).ToLower() == "!die")
                                    {
                                        //if (!msg.Sender.Nickname.ToLower().Contains("baf"))
                                        if (Message.Sender.Hostname.ToLower() != "allegro/botmaster/baf")
                                            Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg(Message.Params[0], "You aren't authorized."));
                                        else
                                        {
                                            Connection.SendMessage(new BAFIRCSharp.Messages.Quit("!die by " + Message.Sender.Nickname + " (" + Message.Sender.Username + "@" + Message.Sender.Hostname + ")"));
                                            while (Connection.SendQueueData)
                                            {
                                                Connection.Poll();
                                                System.Threading.Thread.Sleep(1000);
                                            }
                                            Connection.Disconnect();
                                            Running = false;
                                        }
                                    }
                                    if (Message.Params[1].Length >= 7 && Message.Params[1].Substring(0, 7).ToLower() == "!flushq")
                                    {
                                        Console.WriteLine(Message.Sender.Hostname.ToLower());
                                        //if (!msg.Sender.Nickname.ToLower().Contains("baf"))
                                        if (Message.Sender.Hostname.ToLower() != "allegro/botmaster/baf" && Message.Sender.Hostname.ToLower() != "allegro/user/sevalecan")
                                            Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg(Message.Params[0], "You aren't authorized."));
                                        else
                                        {
                                            Connection.FlushQ();
                                            Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg(Message.Params[0], "Send buffer flushed by " + Message.Sender.Nickname));
                                        }
                                    }
                                    if (Message.Params[1].Length >= 7 && Message.Params[1].Substring(0, 7).ToLower() == "!lschan")
                                    {
                                        Console.WriteLine(Message.Sender.Hostname.ToLower());
                                        //if (!msg.Sender.Nickname.ToLower().Contains("baf"))
                                        if (Message.Sender.Hostname.ToLower() != "allegro/botmaster/baf" && Message.Sender.Hostname.ToLower() != "allegro/user/sevalecan")
                                            Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg(Message.Params[0], "You aren't authorized."));
                                        else
                                        {
                                            Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg(Message.Params[0], "Chan List... requested by " + Message.Sender.Nickname));
                                            foreach (IRCChannel chan in Channels)
                                                Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg(Message.Params[0], chan.ChannelName));
                                        }
                                    }
                                    if (Message.Params[1].Length >= 6 && Message.Params[1].Substring(0, 6).ToLower() == "!lsqry")
                                    {
                                        Console.WriteLine(Message.Sender.Hostname.ToLower());
                                        //if (!msg.Sender.Nickname.ToLower().Contains("baf"))
                                        if (Message.Sender.Hostname.ToLower() != "allegro/botmaster/baf" && Message.Sender.Hostname.ToLower() != "allegro/user/sevalecan")
                                            Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg(Message.Params[0], "You aren't authorized."));
                                        else
                                        {
                                            Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg(Message.Params[0], "Query List... requested by " + Message.Sender.Nickname));
                                            foreach (IRCQuery qry in Queries)
                                                Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg(Message.Params[0], qry.Who.ToString()));
                                        }
                                    }
                                    if (Message.Params[1].Length >= 8 && Message.Params[1].Substring(0, 8).ToLower() == "!dumpbuf")
                                    {
                                        Console.WriteLine(Message.Sender.Hostname.ToLower());
                                        //if (!msg.Sender.Nickname.ToLower().Contains("baf"))
                                        if (Message.Sender.Hostname.ToLower() != "allegro/botmaster/baf" && Message.Sender.Hostname.ToLower() != "allegro/user/sevalecan" && Message.Sender.Hostname.ToLower() != "allegro/user/cgamesplay")
                                            Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg(Message.Params[0], "You aren't authorized."));
                                        else
                                        {
                                            string Arg = Message.Params[1].Substring(9);
                                            List<ChatMessage> ChatBuffer = null;
                                            if (Arg.StartsWith("&") || Arg.StartsWith("#") || Arg.StartsWith("+") || Arg.StartsWith("!"))
                                            {
                                                IRCChannel Chan = FindChannel(Arg);
                                                if (Chan != null)
                                                {
                                                    Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg(Message.Sender.Nickname, "Dumping top 25 lines in channel " + Arg));
                                                    ChatBuffer = Chan.ChatBuffer;
                                                }
                                            }
                                            else
                                            {
                                                IRCQuery Qry = FindQuery(Arg);
                                                if (Qry != null)
                                                {
                                                    Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg(Message.Sender.Nickname, "Dumping top 25 lines in query " + Arg));
                                                    ChatBuffer = Qry.ChatBuffer;
                                                }
                                            }
                                            if (ChatBuffer == null)
                                                Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg(Message.Sender.Nickname, "I dont have any record of " + Arg));
                                            else
                                            {
                                                int Start = 0;
                                                if (ChatBuffer.Count > 25)
                                                    Start = ChatBuffer.Count - 25;

                                                for (int i = Start; i < ChatBuffer.Count; ++i)
                                                {
                                                    switch (ChatBuffer[i].Type)
                                                    {
                                                        case ChatMessage.ChatMessageType.JOIN:
                                                            Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg(Message.Sender.Nickname, ChatBuffer[i].Sender + " joins " + Arg));
                                                            break;
                                                        case ChatMessage.ChatMessageType.KICK:
                                                            Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg(Message.Sender.Nickname, ChatBuffer[i].Sender + " kicked from " + Arg + " by " + ChatBuffer[i].Arg1 + " (" + ChatBuffer[i].Arg2 + ")"));
                                                            break;
                                                        case ChatMessage.ChatMessageType.MODE:
                                                            Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg(Message.Sender.Nickname, "Mode " + Arg + " " + ChatBuffer[i].Arg1 + " set by " + ChatBuffer[i].Sender));
                                                            break;
                                                        case ChatMessage.ChatMessageType.NOTICE:
                                                            Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg(Message.Sender.Nickname, "-" + ChatBuffer[i].Sender.Nickname + "- " + ChatBuffer[i].Arg1));
                                                            break;
                                                        case ChatMessage.ChatMessageType.PART:
                                                            Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg(Message.Sender.Nickname, ChatBuffer[i].Sender.Nickname + " parted " + Arg + "(" + ChatBuffer[i].Arg1 + ")"));
                                                            break;
                                                        case ChatMessage.ChatMessageType.PRIVMSG:
                                                            Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg(Message.Sender.Nickname, "<" + ChatBuffer[i].Sender.Nickname + "> " + ChatBuffer[i].Arg1));
                                                            break;
                                                        case ChatMessage.ChatMessageType.QUIT:
                                                            Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg(Message.Sender.Nickname, ChatBuffer[i].Sender.Nickname + " quit (" + ChatBuffer[i].Arg1 + ")"));
                                                            break;
                                                        case ChatMessage.ChatMessageType.TOPIC:
                                                            Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg(Message.Sender.Nickname, ChatBuffer[i].Sender.Nickname + " sets topic in " + Arg + " to " + ChatBuffer[i].Arg1));
                                                            break;
                                                    }
                                                }
                                                Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg(Message.Sender.Nickname, "The end of " + Arg));
                                            }
                                        }
                                    }
                                    if (Message.Params[1].Length >= 6 && Message.Params[1].Substring(0, 6).ToLower() == "!users")
                                    {
                                        Console.WriteLine(Message.Sender.Hostname.ToLower());
                                        //if (!msg.Sender.Nickname.ToLower().Contains("baf"))
                                        if (Message.Sender.Hostname.ToLower() != "allegro/botmaster/baf" && Message.Sender.Hostname.ToLower() != "allegro/user/sevalecan")
                                            Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg(Message.Params[0], "You aren't authorized."));
                                        else
                                        {
                                            string Arg = Message.Params[1].Substring(7);
                                            IRCChannel Chan = FindChannel(Arg);
                                            if (Chan != null)
                                            {
                                                Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg(Message.Params[0], "Users in " + Arg + " < this list is probably inaccurate > - UserListComplete=" + Chan.UserListComplete));
                                                foreach (IRCChannelUser User in Chan.Users)
                                                    Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg(Message.Params[0], User.Nickname + " status=" + User.Status));
                                            }
                                            else
                                                Connection.SendMessage(new BAFIRCSharp.Messages.PrivMsg(Message.Params[0], "I have no record of " + Arg));
                                        }
                                    }

                                    // Chans start with '&', '#', '+' or '!'
                                    // Route messages to channel
                                    if (Message.Params[0].StartsWith("&") || Message.Params[0].StartsWith("#") || Message.Params[0].StartsWith("+") || Message.Params[0].StartsWith("!"))
                                    {
                                        IRCChannel DestChan = FindChannel(Message.Params[0]);
                                        if (DestChan != null) // If we don't know about the channel, we can blindly ignore it. ;D
                                            DestChan.NewPrivMsg(Message.Sender, Message.Params[1]);
                                    }
                                    else // It's a query or privmsg.
                                    {
                                        IRCQuery Query = FindQuery(Message.Sender.Nickname);
                                        if (Query == null)
                                        {
                                            Query = new IRCQuery(Message.Sender);
                                            Queries.Add(Query);
                                        }
                                        Query.NewPrivMsg(Message.Sender, Message.Params[1]);
                                    }
                                    break;
                                case "NOTICE":
                                    // Chans start with '&', '#', '+' or '!'
                                    // Route messages to channel
                                    if (Message.Params[0].StartsWith("&") || Message.Params[0].StartsWith("#") || Message.Params[0].StartsWith("+") || Message.Params[0].StartsWith("!"))
                                    {
                                        IRCChannel DestChan = FindChannel(Message.Params[0]);
                                        if (DestChan != null) // If we don't know about the channel, we can blindly ignore it. ;D
                                            DestChan.NewNotice(Message.Sender, Message.Params[1]);
                                    }
                                    else // It's a query or privmsg.
                                    {
                                        IRCQuery Query = FindQuery(Message.Sender.Nickname);
                                        if (Query == null)
                                        {
                                            Query = new IRCQuery(Message.Sender);
                                            Queries.Add(Query);
                                        }
                                        Query.NewNotice(Message.Sender, Message.Params[1]);
                                    }
                                    break;
                            }
                        }
                    }
                }

                if (!Connection.Connected && Online)
                {
                    // We got disconnected
                    Console.WriteLine("Disconnected");
                    foreach (IRCChannel Chan in Channels)
                    {
                        Chan.Users.Clear();
                        Chan.UserListComplete = false;
                    }
                    Online = false;
                    DoConnect();
                }

                Thread.Sleep(100);
            }
        }

        // Find a channel or query
        IRCChannel FindChannel(string Name)
        {
            foreach (IRCChannel Chan in Channels)
            {
                if (Chan.ChannelName.ToLower() == Name.ToLower())
                    return Chan;
            }
            return null;
        }
        IRCQuery FindQuery(string Name)
        {
            foreach (IRCQuery Query in Queries)
            {
                if (Query.Who.Nickname.ToLower() == Name.ToLower())
                    return Query;
            }
            return null;
        }
    }
}
